/*
 * Copyright (c) Facebook, Inc. and its affiliates.
 *
 * This source code is licensed under the MIT license found in the
 * LICENSE file in the root directory of this source tree.
 */

package com.facebook.drawee.drawable;

import com.facebook.common.internal.Preconditions;
import ohos.agp.components.Component;
import ohos.agp.components.element.Element;
import ohos.agp.render.Canvas;
import ohos.agp.render.ColorFilter;
import ohos.agp.utils.Matrix;
import ohos.agp.utils.Rect;
import ohos.agp.utils.RectFloat;

import javax.annotation.Nullable;

/**
 * A Drawable that contains an array of other Drawables (layers). These are drawn in array order, so
 * the element with the largest index will be drawn on top.
 *
 * <p>Similar to LayerDrawable but it doesn't support adding/removing layers dynamically.
 */
public class ArrayDrawable extends RootShapeElement
    implements TransformCallback, TransformAwareDrawable,Element.OnChangeListener,Callback {

  private final String TAG = ArrayDrawable.class.getSimpleName();
  private TransformCallback mTransformCallback;
  protected Canvas mCanvas;

  private final DrawableProperties mDrawableProperties = new DrawableProperties();
  // layers
  private final Element[] mLayers;
  // drawable parents for the layers (lazily created)
  private final DrawableParent[] mDrawableParents;

  // temp rect to avoid allocations
  private final Rect mTmpRect = new Rect();

  // Whether the drawable is stateful or not
  private boolean mIsStateful = false;
  private boolean mIsStatefulCalculated = false;

  private boolean mIsMutated = false;

  public Component mComponent;

  /**
   * Constructs a new layer drawable.
   *
   * @param layers the layers that this drawable displays
   */
  public ArrayDrawable(Element[] layers) {
      //Logutil.error(TAG,"ArrayDrawable: visible"+getVisible());
    Preconditions.checkNotNull(layers);
    mLayers = layers;
    for (int i = 0; i < mLayers.length; i++) {
      DrawableUtils.setCallbacks(mLayers[i], this,this, this);
    }
    mDrawableParents = new DrawableParent[mLayers.length];
  }

  public void setComponent(Component component){
    this.mComponent = component;
  }

  /**
   * Gets the number of layers.
   *
   * @return number of layers
   */
  public int getNumberOfLayers() {
    return mLayers.length;
  }

  /**
   * Gets the drawable at the specified index.
   *
   * @param index index of drawable to get
   * @return drawable at the specified index
   */
  @Nullable
  public Element getDrawable(int index) {
    Preconditions.checkArgument(index >= 0);
    Preconditions.checkArgument(index < mLayers.length);
    return mLayers[index];
  }

  /** Sets a new drawable at the specified index, and return the previous drawable, if any. */
  @Nullable
  public Element setDrawable(int index, @Nullable Element drawable) {
    Preconditions.checkArgument(index >= 0);
    Preconditions.checkArgument(index < mLayers.length);
    final Element oldDrawable = mLayers[index];
    if (drawable != oldDrawable) {
      if (drawable != null && mIsMutated) {
//        drawable.mutate();
      }

      DrawableUtils.setCallbacks(mLayers[index], null,null, null);
      DrawableUtils.setCallbacks(drawable, null,null, null);
      DrawableUtils.setDrawableProperties(drawable, mDrawableProperties);
      DrawableUtils.copyProperties(drawable, this);
      DrawableUtils.setCallbacks(drawable, this,this, this);
      mIsStatefulCalculated = false;
      mLayers[index] = drawable;
      invalidateSelf();
    }
    return oldDrawable;
  }

  public void invalidateSelf(){
    //Logutil.error(TAG,"invalidateSelf");
    if(getCallback()!=null){
      getCallback().onChange(this);
    }

    /*
    if(mComponent != null){
      //Logutil.error(TAG,"invalidateSelf start to draw!");
      //mComponent.addDrawTask(((component, canvas) -> drawToCanvas(canvas)));
      mComponent.invalidate();
    }

     */
  }

  @Override
  public int getWidth() {
    int width = -1;
    for (int i = 0; i < mLayers.length; i++) {
      Element drawable = mLayers[i];
      if (drawable != null) {
        width = Math.max(width, drawable.getWidth());
      }
    }
    //Logutil.error(TAG,"getWidth:"+width);
    return width > 0 ? width : -1;
  }

  @Override
  public int getHeight() {
    int height = -1;
    for (int i = 0; i < mLayers.length; i++) {
      Element drawable = mLayers[i];
      if (drawable != null) {
        height = Math.max(height, drawable.getHeight());
      }
    }
    //Logutil.error(TAG,"getHeight:"+height);
    return height > 0 ? height : -1;
  }

  public void setBounds(Rect rect) {
    //Logutil.error(TAG,"setBounds:"+rect);
    Rect oldBounds = getBounds();
    if (oldBounds.left != rect.left ||
            oldBounds.top != rect.top ||
            oldBounds.right != rect.right ||
            oldBounds.bottom != rect.bottom) {
      for (Element drawable : mLayers) {
        if (drawable != null) {
          drawable.setBounds(rect);
        }
      }
    }
    super.setBounds(rect);
    /*
    for (int i = 0; i < mLayers.length; i++) {
      Element drawable = mLayers[i];
      if (drawable != null) {
        drawable.setBounds(rect);
      }
    }
    super.setBounds(rect);
    */
  }


  @Override
  public boolean isStateful() {
    super.isStateful();
    if (!mIsStatefulCalculated) {
      mIsStateful = false;
      for (int i = 0; i < mLayers.length; i++) {
        Element drawable = mLayers[i];
        mIsStateful |= drawable != null && drawable.isStateful();
      }
      mIsStatefulCalculated = true;
    }
    return mIsStateful;
  }

//  @Override
//  protected boolean onStateChange(int[] state) {
//    boolean stateChanged = false;
//    for (int i = 0; i < mLayers.length; i++) {
//      Element drawable = mLayers[i];
//      if (drawable != null && drawable.setState(state)) {
//        stateChanged = true;
//      }
//    }
//    return stateChanged;
//  }

//  @Override
//  protected boolean onLevelChange(int level) {
//    boolean levelChanged = false;
//    for (int i = 0; i < mLayers.length; i++) {
//      Drawable drawable = mLayers[i];
//      if (drawable != null && drawable.setLevel(level)) {
//        levelChanged = true;
//      }
//    }
//    return levelChanged;
//  }


  @Override
  public void drawToCanvas(Canvas canvas) {
    mCanvas = canvas;
    if(mLayers!=null) {
      //Logutil.error(TAG, "drawToCanvas A  layers.length:" + mLayers.length);
      for (int i = 0; i < mLayers.length; i++) {
        Element drawable = mLayers[i];
        //Logutil.error(TAG, "drawToCanvas B index:" + i);
        if (drawable != null) {
          //Logutil.error(TAG, "drawToCanvas :" + i);
          drawable.drawToCanvas(canvas);
        }
      }
    }else{
      //Logutil.error(TAG,"drawToCanvas layers is null");
    }
  }

//  @Override
//  public boolean getPadding(Rect padding) {
//    padding.left = 0;
//    padding.top = 0;
//    padding.right = 0;
//    padding.bottom = 0;
//    final Rect rect = mTmpRect;
//    for (int i = 0; i < mLayers.length; i++) {
//      Element drawable = mLayers[i];
//      if (drawable != null) {
//        drawable.getPadding(rect);
//        padding.left = Math.max(padding.left, rect.left);
//        padding.top = Math.max(padding.top, rect.top);
//        padding.right = Math.max(padding.right, rect.right);
//        padding.bottom = Math.max(padding.bottom, rect.bottom);
//      }
//    }
//    return true;
//  }

//  @Override
//  public Drawable mutate() {
//    for (int i = 0; i < mLayers.length; i++) {
//      Drawable drawable = mLayers[i];
//      if (drawable != null) {
//        drawable.mutate();
//      }
//    }
//    mIsMutated = true;
//    return this;
//  }
//
//  @Override
//  public int getOpacity() {
//    if (mLayers.length == 0) {
//      return PixelFormat.TRANSPARENT;
//    }
//    int opacity = PixelFormat.OPAQUE;
//    for (int i = 1; i < mLayers.length; i++) {
//      Drawable drawable = mLayers[i];
//      if (drawable != null) {
//        opacity = Drawable.resolveOpacity(opacity, drawable.getOpacity());
//      }
//    }
//    return opacity;
//  }

  @Override
  public void setAlpha(int alpha) {
    mDrawableProperties.setAlpha(alpha);
    for (int i = 0; i < mLayers.length; i++) {
      Element drawable = mLayers[i];
      if (drawable != null) {
        drawable.setAlpha(alpha);
      }
    }
  }

  @Override
  public void setColorFilter(ColorFilter colorFilter) {
    mDrawableProperties.setColorFilter(colorFilter);
    for (int i = 0; i < mLayers.length; i++) {
      Element drawable = mLayers[i];
      if (drawable != null) {
        if(drawable instanceof RootShapeElement) {
          ((RootShapeElement) drawable).setColorFilter(colorFilter);
        }
      }
    }
  }

//  @Override
//  public void setDither(boolean dither) {
//    mDrawableProperties.setDither(dither);
//    for (int i = 0; i < mLayers.length; i++) {
//      Drawable drawable = mLayers[i];
//      if (drawable != null) {
//        drawable.setDither(dither);
//      }
//    }
//  }
//
//  @Override
//  public void setFilterBitmap(boolean filterBitmap) {
//    mDrawableProperties.setFilterBitmap(filterBitmap);
//    for (int i = 0; i < mLayers.length; i++) {
//      Drawable drawable = mLayers[i];
//      if (drawable != null) {
//        drawable.setFilterBitmap(filterBitmap);
//      }
//    }
//  }

  @Override
  public boolean setVisible(boolean visible, boolean restart) {
    //Logutil.error(TAG,"setVisible curVisible:"+getVisible()+ " setVisible:"+visible);
    boolean changed = super.setVisible(visible, restart);
    //Logutil.error(TAG,"setVisible:"+visible+ "   restart:"+restart+"   changed:"+changed+ "  curVisible:"+getVisible());
    for (int i = 0; i < mLayers.length; i++) {
      Element drawable = mLayers[i];
      if (drawable != null) {
        drawable.setVisible(visible, restart);
      }
    }
    return changed;
  }

  /** Gets the {@code DrawableParent} for index. */
  public DrawableParent getDrawableParentForIndex(int index) {
    Preconditions.checkArgument(index >= 0);
    Preconditions.checkArgument(index < mDrawableParents.length);
    if (mDrawableParents[index] == null) {
      mDrawableParents[index] = createDrawableParentForIndex(index);
    }
    return mDrawableParents[index];
  }

  private DrawableParent createDrawableParentForIndex(final int index) {
    return new DrawableParent() {
      @Override
      public Element setDrawable(Element newDrawable) {
        return ArrayDrawable.this.setDrawable(index, newDrawable);
      }

      @Override
      public Element getDrawable() {
        return ArrayDrawable.this.getDrawable(index);
      }
    };
  }

  /** Drawable.Callback methods */
//  @Override
  public void invalidateDrawable(Element who) {
    if(mComponent != null)
    invalidateSelf();
  }

  @Override
  public void scheduleDrawable(Element who, Runnable what, long when) {
      scheduleSelf(what,when);

  }

  @Override
  public void unscheduleDrawable(Element who, Runnable what) {
    unscheduleSelf(what);

  }
//
//  @Override
//  public void scheduleDrawable(Element who, Runnable what, long when) {
//    what = new Runnable() {
//      @Override
//      public void run() {
//        invalidateSelf();
//      }
//    };
//    what.run();
////    scheduleSelf(what, when);
//  }
//
//  @Override
//  public void unscheduleDrawable(Element who, Runnable what) {
//  }

  /** TransformationCallbackSetter method */
  @Override
  public void setTransformCallback(TransformCallback transformCallback) {
    mTransformCallback = transformCallback;
  }

  /** TransformationCallback methods */
  @Override
  public void getTransform(Matrix transform) {
    if (mTransformCallback != null) {
      mTransformCallback.getTransform(transform);
    } else {
      transform.reset();
    }
  }

  @Override
  public void getRootBounds(RectFloat bounds) {
    if (mTransformCallback != null) {
      mTransformCallback.getRootBounds(bounds);
    } else {
      bounds.left = getBounds().left;
      bounds.right = getBounds().right;
      bounds.top = getBounds().top;
      bounds.bottom = getBounds().bottom;
//      bounds.set(getBounds());
    }
  }

//  @Override
//  @TargetApi(Build.VERSION_CODES.LOLLIPOP)
//  public void setHotspot(float x, float y) {
//    for (int i = 0; i < mLayers.length; i++) {
//      Drawable drawable = mLayers[i];
//      if (drawable != null) {
//        drawable.setHotspot(x, y);
//      }
//    }
//  }

  @Override
  public void onChange(Element element) {
    invalidateSelf();
  }
}
